package main

import (
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	business "example.com/m/com.business"
	qfcfg "example.com/m/com.qfcfg"
	qfmysql "example.com/m/com.qfmysql"
	qfredis "example.com/m/com.qfredis"
	"example.com/m/internal/db"
	"example.com/m/util"
	_ "github.com/go-sql-driver/mysql"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"
	"github.com/kataras/iris/v12/core/router"
	"github.com/qf-tech/qftech-base/pkg/log"
	qflog "github.com/qf-tech/qftech-base/pkg/log"
	"github.com/qf-tech/qftech-base/pkg/qfcrypt"
)

func regionLimitHandler(c iris.Context) {
	start := time.Now()
	h := business.Controller{}
	h.CheckTraceID(c)
	c.Next()
	// 统计时间
	since := time.Since(start)
	log := log.GetLogger().GetCtx(c.Request().Context()).Sugar()
	log.Infof("cost: %v", since)
}

// middleWare 生成traceID、统计接口耗时
func middleWare(c iris.Context) {
	// start := time.Now()
	h := business.Controller{}
	// h.CheckTraceID(c)

	// 获取日志、报告进行 token 鉴权
	if c.Method() == "GET" && strings.HasPrefix(c.Request().RequestURI, "/cava3/outputs/log") {
		if err := h.CheckReportToken(c); err != nil {
			business.JSONError(c, http.StatusBadRequest, err.Error())
			return
		}
	}
	c.Next()
}

// Cors 跨域请求处理
func Cors(ctx iris.Context) {
	ctx.Header("Access-Control-Allow-Origin", "*")
	if ctx.Request().Method == "OPTIONS" {
		ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS")
		ctx.Header("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization")
		ctx.StatusCode(204)
		return
	}
	ctx.Next()
}

func DirList(ctx *context.Context, dirOptions router.DirOptions, dirName string, dir http.File) error {
	log := log.GetLogger().GetCtx(ctx.Request().Context()).Sugar()
	htmlReplacer := strings.NewReplacer(
		"&", "&amp;",
		"<", "&lt;",
		">", "&gt;",
		// "&#34;" is shorter than "&quot;".
		`"`, "&#34;",
		// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
		"'", "&#39;",
	)

	dirs, err := dir.Readdir(-1)
	if err != nil {
		return err
	}
	sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() })
	ctx.ContentType(context.ContentHTMLHeaderValue)

	tmpURI := strings.TrimSuffix(strings.Split(ctx.Request().RequestURI, "?")[0], "/")
	pos := strings.LastIndex(tmpURI, "/")
	parentURI := ""
	if pos > -1 {
		parentURI = tmpURI[:pos+1]
	}
	log.Infof("uri: %s, parentURI: %s", ctx.Request().RequestURI, parentURI)
	data := make([]map[string]string, len(dirs)+1)
	data[0] = map[string]string{
		"HREF":    parentURI,
		"NAME":    "..",
		"SIZE":    "-",
		"MODTIME": "-",
	}

	for i, d := range dirs {
		name := d.Name()
		if d.IsDir() {
			name += "/"
		}

		url := url.URL{Path: path.Join("./"+dirName, name)} // edit here to redirect correctly, standard library misses that.
		data[i+1] = map[string]string{
			"HREF":    url.String(),
			"NAME":    htmlReplacer.Replace(name),
			"SIZE":    util.TransBytesToOther(d.Size()),
			"MODTIME": d.ModTime().Format("2006-01-02 15:04:05"),
		}
	}
	contentBody, err := util.MakeBodyFromTemplateV2(
		"./files.html.tmpl",
		data,
	)
	if err != nil {
		log.Errorf("make email content error: %v", err)
		return err
	}
	retConBody := strings.ReplaceAll(string(contentBody), "\n", "")
	_, _ = ctx.WriteString(retConBody)
	return err
}

func newRouter() *iris.Application {
	app := iris.New()
	// app.Use(iris.Logger())
	// app.Use(app.Recovery())
	// 注册中间件
	app.Use(Cors)
	app.Use(middleWare)

	app.HandleDir("/cava3/outputs/log", "./cava3/outputs/log", iris.DirOptions{
		ShowList: true,
		DirList:  DirList,
	})

	fileSvr := qfcfg.ServerConfig.Server.FileSvr
	if fileSvr.URL != "" && fileSvr.Dir != "" {
		app.HandleDir(fileSvr.URL, fileSvr.Dir, iris.DirOptions{
			ShowList: true,
			DirList:  DirList,
		})
	}

	h := business.Controller{}
	api := app.Party("/", regionLimitHandler)
	{
		api.Get("/notice", h.NoticeHandler)
		api.Get("/redis", h.RedisHandler)
		api.Get("/cava3/upload/{filename:string}", h.DownloadHandler)
		api.Get("/lock", h.LockHandler)
		api.Get("/iredmail/queue", h.IRedMailQueue)
		api.Get("/cava3/task/{uid:string}", h.GetTask)
		api.Get("/cava3/tasks/info", h.GetTaskV2)
		api.Get("/cava3/ipinfo", h.GetIPInfo)

		api.Post("/authorization", h.AuthHandler)
		api.Post("/register", h.RegistHandler)
		api.Post("/encrypt", h.EncryptHandler)
		api.Post("/decrypt", h.DecryptHandler)
		api.Post("/cava3/upload", h.UploadHandler)
		api.Post("/cava3/convert", h.ConvertHandler)
		api.Post("/cava3/task", h.WsseHandler, h.UpsertTask)
		api.Post("/cava3/screenshot", h.Screenshot)
		api.Get("/sign/update", h.UpdateSign)
		api.Get("/sign/verify", h.VerifySign)
	}

	return app
}

func httpServer() {
	address := fmt.Sprintf("%s:%d", qfcfg.ServerConfig.Server.IP, qfcfg.ServerConfig.Server.Port)

	router := newRouter()
	if err := router.Run(iris.Addr(address)); err != nil {
		qflog.Sugare.Errorf("listen err: %v", err)
		os.Exit(1)
	}
	log.Sugare.Warnf("app run and listen on: %s", address)
}

func initCrypt() {
	currentVersionFlag := "QFTECH"
	key := "lmN4dtPyeC5r29DYBLl0P0OoA4Afy/2UnCg0zd+hHhg="

	oldKeys := make(map[string][]byte)
	zeroKeytr := "q_1dY=Khec2nMNxV"
	oldKeys[qfcrypt.ZeroVersionFlag] = []byte(zeroKeytr)

	_ = qfcrypt.Init(currentVersionFlag, []byte(key), oldKeys)
}

func main() {
	logConfig := &qflog.LogConfig{
		MaxCount: 60,
		MaxSize:  20,
		Compress: true,
		FilePath: "./log/server.log",
		Level:    qflog.InfoLevel,
		Format:   qflog.JsonType,
	}
	qflog.Init(logConfig)

	initCrypt()
	qfcfg.InitConfig()

	// 打印版本信息
	path, _ := os.Executable()
	_, svrName := filepath.Split(path)
	flag.Parse()
	if option.PrintVersion {
		fmt.Printf("%s %s %s (Git SHA: %s, Go Version: %s, BuildAt: %s)\n", svrName, GitBranch, Version, GitSHA, runtime.Version(), BuildAt)
		os.Exit(0)
	}

	_ = qfredis.InitRedisPool()
	if err := qfmysql.InitMysqlDB(); err != nil {
		log.Sugare.Fatalf("init mysql err: %v", err)
		return
	}

	err := db.Init()
	if err != nil {
		log.Sugare.Errorf("mysql db init failed, err: %v", err)
		return
	}

	go httpServer()
	select {}
}
